package cn.jhz.learn.blog.component.shiro.realm;

import cn.jhz.learn.blog.component.shiro.JwtToken;
import cn.jhz.learn.blog.po.account.AccountRole;
import cn.jhz.learn.blog.po.account.AccountUser;
import cn.jhz.learn.blog.service.security.permission.PermissionService;
import cn.jhz.learn.blog.service.security.permission.RoleService;
import cn.jhz.learn.blog.service.security.permission.UserService;
import com.google.common.collect.Sets;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 使用基于项目环境持久化层中的用户帐户和角色来支持身份验证和授权的Realm接口的简单实现,JwtToken.<br>
 * 每个帐户条目指定用户的用户名、密码和角色.还可以将角色映射到权限并与用户关联.<br>
 * 不对用户帐号和角色进行存储.<br>
 * @author machine_005
 * @version 1.0
 */
public class JwtRealm extends AuthorizingRealm {

    private final UserService userService;
    private final PermissionService permissionService;
    private final RoleService roleService;

    @Autowired
    public JwtRealm(UserService userService, PermissionService permissionService, RoleService roleService) {
        this.userService = userService;
        this.permissionService = permissionService;
        this.roleService = roleService;
    }

    @Override
    public String getName() {
        return "jwtRealm";
    }

    /**
     * 对给定AuthenticationToken实例表示的主题进行身份验证,限定这个Realm只处理JwtToken
     * @param token 为身份验证尝试提交的AuthenticationToken
     * @return <tt>true</tt> 如果此域可以对指定令牌表示的主题进行身份验证
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 从基于项目环境持久化层中检索给定主体的AuthorizationInfo.
     * @param principals 应该检索的AuthorizationInfo的主要标识主体.
     * @return 与此主体关联的AuthorizationInfo.
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Set<String> roles = Sets.newConcurrentHashSet();
        Set<String> permissions = Sets.newConcurrentHashSet();
        AccountUser user = (AccountUser) principals.getPrimaryPrincipal();

        if("machine005@admin.cn".equals(user.getLoginId())){
            /*拥有所有权限*/
            permissions.add("*:*");
            /*添加角色*/
            roles.add("admin");
        }else{
            Set<AccountRole> rolesTemp;
            rolesTemp = roleService.getRolesByUserId(user.getId());
            permissions.addAll(permissionService.getPermissionResourceByRoles(rolesTemp));
            roles.addAll(rolesTemp.stream().map(AccountRole::getName).collect(Collectors.toSet()));
        }

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(permissions);
        info.addRoles(roles);

        return info;
    }

    /**
     * 为给定的身份验证令牌从项目的持久化层中检索身份验证数据.
     * 剩下的校验工作, 由shiro完成.<br>
     * null返回值意味着不能将任何帐户与指定的令牌关联.<br>
     * ?此处的SimpleAuthenticationInfo可返回任意值,密码校验时不会用到它.
     * @param token 包含用户主体和凭据的身份验证令牌,登录时包装的UsernamePasswordToken.
     * @return AuthenticationInfo对象,其中包含只有在查找成功时才会产生的帐户数据(例如,帐户存在且有效,等等).
     * @throws AuthenticationException 如果认证账户被锁定或JwtToken参数异常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        if (token.getPrincipal() != null) {
            String loginId = ((List<String>)token.getPrincipal()).get(0);
            AccountUser user = userService.getUserByLoginId(loginId);

            if(user != null)
                /*用户被锁定*/
                if (user.getStatus() != 0)
                   return new SimpleAuthenticationInfo(user, loginId, getName());
                else
                     throw new LockedAccountException("该用户已被锁定,暂时无法登录！");
            else
                throw new UnknownAccountException("用户不存在！");


        }
        else
            throw new AccountException("JWT token参数异常！:LoginId为null");
    }
}
